﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Odelle.Utils.Comparisons;

namespace Odelle.Utils.Comparisons
{
    public class FlexibleStringComparator : StringComparator
    {
        #region Properties

        public enum QueryOperator
        {
            QueryFlexible,                      // - prefix (or suffix) for flex-query -

            QueryAllPass,                       // "*"
            QueryAllBlock,

            QueryEqual,                         // = or (none)
            QueryContains,                      // *.......*
            QueryStartsWith,                    // ........*
            QueryEndsWith,                      // *
            QueryOfComponent,                   // +

            QueryEqual_CaseInsensitive,         // @
            QueryContains_CaseInsensitive,      // @*.......*
            QueryStartsWith_CaseInsensitive,    // @........*
            QueryEndsWith_CaseInsensitive,      // @*
            QueryOfComponent_CaseInsensitive,   // @+

            QueryEqual_AsNumber,                // %= or %
            QueryGreaterThan_AsNumber,          // %>
            QueryGreaterOrEqual_AsNumber,       // %>=
            QueryLessThan_AsNumber,             // %<
            QueryLessOrEqual_AsNumber           // %<=
        }

        private QueryOperator _defaultOperator = QueryOperator.QueryFlexible;
        public QueryOperator DefaultOperator
        {
            set { _defaultOperator = value; }
            get { return _defaultOperator; }
        }

        #endregion

        private void UpdateflexParameters(ref string value, ref string formatValue, ref QueryOperator queryOperator)
        { 
            bool isCaseInsensitive = false;
            bool asNumber = false;

            if (formatValue.StartsWith("%")) // as number
            {
                formatValue = formatValue.Substring(1);

                if (formatValue.StartsWith(">="))
                { 
                    queryOperator = QueryOperator.QueryGreaterOrEqual_AsNumber;
                    formatValue = formatValue.Substring(2);
                }
                else if (formatValue.StartsWith(">"))
                {
                    queryOperator = QueryOperator.QueryGreaterThan_AsNumber;
                    formatValue = formatValue.Substring(1);
                }
                else if (formatValue.StartsWith("<="))
                {
                    queryOperator = QueryOperator.QueryLessOrEqual_AsNumber;
                    formatValue = formatValue.Substring(2);
                }
                else if (formatValue.StartsWith("<"))
                {
                    queryOperator = QueryOperator.QueryLessThan_AsNumber;
                    formatValue = formatValue.Substring(1);
                }
                else if (formatValue.StartsWith("="))
                {
                    queryOperator = QueryOperator.QueryEqual_AsNumber;
                    formatValue = formatValue.Substring(1);
                }
                else
                {
                    queryOperator = QueryOperator.QueryEqual_AsNumber;
                }

                asNumber = true;
            }

            if (!asNumber)
            {
                if (formatValue.StartsWith("@")) // case insensitive
                {
                    isCaseInsensitive = true;
                    formatValue = formatValue.Substring(1);
                }

                if (formatValue == "*")
                {
                    queryOperator = QueryOperator.QueryAllPass;
                    formatValue = "";
                }
                else if (formatValue.StartsWith("*") && formatValue.EndsWith("*"))
                {
                    if (isCaseInsensitive)
                        queryOperator = QueryOperator.QueryContains_CaseInsensitive;
                    else
                        queryOperator = QueryOperator.QueryContains;

                    formatValue = formatValue.Substring(1, formatValue.Length - 2);
                }
                else if (formatValue.StartsWith("*"))
                {
                    if (isCaseInsensitive)
                        queryOperator = QueryOperator.QueryEndsWith_CaseInsensitive;
                    else
                        queryOperator = QueryOperator.QueryEndsWith;

                    formatValue = formatValue.Substring(1);
                }
                else if (formatValue.EndsWith("*"))
                {
                    if (isCaseInsensitive)
                        queryOperator = QueryOperator.QueryStartsWith_CaseInsensitive;
                    else
                        queryOperator = QueryOperator.QueryStartsWith;

                    formatValue = formatValue.Substring(0, formatValue.Length - 1);
                }
                else if (formatValue.StartsWith("+"))
                {
                    if (isCaseInsensitive)
                        queryOperator = QueryOperator.QueryOfComponent_CaseInsensitive;
                    else
                        queryOperator = QueryOperator.QueryOfComponent;

                    formatValue = formatValue.Substring(1);
                }
                else if (formatValue.StartsWith("="))
                {
                    if (isCaseInsensitive)
                        queryOperator = QueryOperator.QueryEqual_CaseInsensitive;
                    else
                        queryOperator = QueryOperator.QueryEqual;

                    formatValue = formatValue.Substring(1);
                }
                else
                {
                    if (isCaseInsensitive)
                        queryOperator = QueryOperator.QueryEqual_CaseInsensitive;
                    else
                        queryOperator = QueryOperator.QueryEqual;
                }
            }

            if (queryOperator == QueryOperator.QueryFlexible)
                queryOperator = QueryOperator.QueryEqual;
        }

        public override bool Compare(string value, string formatValue)
        {
            return Compare(value, formatValue, QueryOperator.QueryFlexible);
        }
        
        public bool Compare(string value, string formatValue, QueryOperator queryOperator)
        {
            if (queryOperator == QueryOperator.QueryFlexible)
            {
                UpdateflexParameters(ref  value, ref  formatValue, ref  queryOperator);
            }

            if (queryOperator == QueryOperator.QueryAllPass)
            {
                return true;
            }

            if (queryOperator == QueryOperator.QueryAllBlock)
            {
                return false;
            }

            double dblValue = 0.0;
            double dblAttribute = 0.0;
            bool areNumbers = false;

           areNumbers = (double.TryParse(formatValue, out dblValue) && double.TryParse(value, out dblAttribute));

            switch (queryOperator)
            {
                case QueryOperator.QueryEqual:
                    if (value == formatValue)
                    {
                        return true;
                    }
                    break;

                case QueryOperator.QueryContains:
                    if (formatValue != "")
                    {
                        if (value.Contains(formatValue))
                        {
                            return true;
                        }
                    }
                    break;

                case QueryOperator.QueryStartsWith:
                    if (formatValue != "")
                    {
                        if (value.StartsWith(formatValue))
                        {
                            return true;
                        }
                    }
                    break;

                case QueryOperator.QueryEndsWith:
                    if (formatValue != "")
                    {
                        if (value.EndsWith(formatValue))
                        {
                            return true;
                        }
                    }
                    break;

                case QueryOperator.QueryOfComponent:
                    if (formatValue != "")
                    {
                        if (formatValue.Contains(value))
                        {
                            return true;
                        }
                    }
                    break;

                // not case sensitive
                case QueryOperator.QueryEqual_CaseInsensitive:
                    if (value.ToLower() == formatValue.ToLower())
                    {
                        return true;
                    }
                    break;

                case QueryOperator.QueryContains_CaseInsensitive:
                    if (formatValue != "")
                    {
                        if (value.ToLower().Contains(formatValue.ToLower()))
                        {
                            return true;
                        }
                    }
                    break;

                case QueryOperator.QueryStartsWith_CaseInsensitive:
                    if (formatValue != "")
                    {
                        if (value.ToLower().StartsWith(formatValue.ToLower()))
                        {
                            return true;
                        }
                    }
                    break;

                case QueryOperator.QueryEndsWith_CaseInsensitive:
                    if (formatValue != "")
                    {
                        if (value.ToLower().EndsWith(formatValue.ToLower()))
                        {
                            return true;
                        }
                    }
                    break;

                case QueryOperator.QueryOfComponent_CaseInsensitive:
                    if (formatValue != "")
                    {
                        if (formatValue.ToLower().Contains(value.ToLower()))
                        {
                            return true;
                        }
                    }
                    break;

                // compare as number
                case QueryOperator.QueryEqual_AsNumber:
                    if (areNumbers)
                    {
                        if (dblAttribute == dblValue)
                        {
                            return true;
                        }
                    }
                    break;

                case QueryOperator.QueryGreaterThan_AsNumber:
                    if (areNumbers)
                    {
                        if (dblAttribute > dblValue)
                        {
                            return true;
                        }
                    }
                    break;

                case QueryOperator.QueryGreaterOrEqual_AsNumber:
                    if (areNumbers)
                    {
                        if (dblAttribute >= dblValue)
                        {
                            return true;
                        }
                    }
                    break;

                case QueryOperator.QueryLessThan_AsNumber:
                    if (areNumbers)
                    {
                        if (dblAttribute < dblValue)
                        {
                            return true;
                        }
                    }
                    break;

                case QueryOperator.QueryLessOrEqual_AsNumber:
                    if (areNumbers)
                    {
                        if (dblAttribute <= dblValue)
                        {
                            return true;
                        }
                    }
                    break;

                default:
                    break;
            }

            return false;
        }

    }
}
